import EventEmitter from "../eventEmitter";
import RectNodeGraph from "../shape/rect";
import PolyLineNodeGraph from "../shape/polyline";
import BezierCurveNodeGraph from "../shape/bezierCurve";
import GraphBase from "../shape/graphBase";
import { Point } from "@/shape/dependencies";
import { throttle } from "../utils";
import { getScale } from "@/graphviewState";
export default class LineControlPoints extends EventEmitter {
  target: HTMLCanvasElement;
  ctx: CanvasRenderingContext2D | null;
  // 记录所有的控制点
  pointMap = new Map();
  pointSize = 10;
  pointColor = "rgb(200,200,200)";
  // 当前鼠标悬停的控制点(index用于记录在线条中点的索引位置,target是控制点)
  hoverControlPoint!: {
    index: number;
    target: RectNodeGraph;
  } | null;
  // 是否左键按下
  isLeftKeyDown = false;
  // 控制点控制的图元对象
  controlTarget: BezierCurveNodeGraph | PolyLineNodeGraph | null | undefined;
  mouseStartPosition: { x: number; y: number } = { x: 0, y: 0 };
  // 当前控制器是否处于激活状态
  activity = false;
  // 前控制点,贝塞尔曲线使用
  preControlPoint = new RectNodeGraph();
  // 当前调整控制点的位置点(index用于记录在线条中点的索引位置,target是控制点)
  currentControlPoint: {
    index: number;
    target: RectNodeGraph;
  } | null = null;
  // 后控制点,贝塞尔曲线使用
  nextControlPoint = new RectNodeGraph();
  // 用于区分鼠标双击和鼠标按下抬起
  mouseDownTime = 0;
  mouseUpTime = 0;
  interval = 200;
  // 记录当前鼠标所在的贝塞尔曲线的控制点
  hoverBezierControlPoint: null | {
    type: string;
    target: RectNodeGraph;
  } = null;
  constructor(target: HTMLCanvasElement) {
    super();
    this.target = target;
    this.ctx = target.getContext("2d");
    this.init();
  }
  init() {
    this.initInteractor();
    this.initBezierCOntrolPoints();
  }
  // 根据控制图元,更新控制点的位置
  resetControlPoints() {
    if (!this.ctx || !this.controlTarget) {
      return;
    }
    if (this.controlTarget instanceof PolyLineNodeGraph) {
      this.controlTarget.points.forEach((point: Point, index: number) => {
        this.pointMap.get(index).setPosition(point.x, point.y);
      });
    } else if (this.controlTarget instanceof BezierCurveNodeGraph) {
      for (let i = 0; i < this.controlTarget.points.length; i += 3) {
        const point = this.controlTarget.points[i];
        this.pointMap.get(i).setPosition(point.x, point.y);
      }
    }
  }
  // 重新生成控制点
  initControlPoints() {
    this.pointMap.clear();
    if (!this.ctx || !this.controlTarget) {
      return;
    }
    if (this.controlTarget instanceof PolyLineNodeGraph) {
      this.controlTarget.points.forEach((point: Point, index: number) => {
        const rect = new RectNodeGraph();
        rect.setCtx(this.ctx);
        rect.setWidth(this.pointSize);
        rect.setHeight(this.pointSize);
        rect.setBackgroundColor(this.pointColor);
        rect.setPosition(point.x, point.y);
        this.pointMap.set(index, rect);
      });
    } else if (this.controlTarget instanceof BezierCurveNodeGraph) {
      for (let i = 0; i < this.controlTarget.points.length; i += 3) {
        const point = this.controlTarget.points[i];
        const rect = new RectNodeGraph();
        rect.setCtx(this.ctx);
        rect.setWidth(this.pointSize);
        rect.setHeight(this.pointSize);
        rect.setBackgroundColor(this.pointColor);
        rect.setPosition(point.x, point.y);
        this.pointMap.set(i, rect);
      }
    }
  }
  // 初始化贝塞尔曲线的前后控制点
  initBezierCOntrolPoints() {
    const pre = this.preControlPoint;
    const next = this.nextControlPoint;
    [pre, next].forEach((rect) => {
      rect.setCtx(this.ctx);
      rect.setWidth(6);
      rect.setHeight(6);
      rect.setBackgroundColor("rgb(64,158,255)");
      rect.setPosition(0, 0);
    });
  }
  setCursor(cursorStyle: string) {
    this.target.style.cursor = cursorStyle;
  }
  setControlTarget(target: GraphBase[] | null | undefined) {
    if (target) {
      const ctarget =
        target[0].type === "polyline"
          ? (target[0] as PolyLineNodeGraph)
          : (target[0] as BezierCurveNodeGraph);
      // 如果控制的图元没变,则不用重置控制点
      if (this.controlTarget !== ctarget) {
        this.controlTarget = ctarget;
        this.initControlPoints();
      } else {
        this.resetControlPoints();
      }
    } else {
      this.controlTarget = null;
      this.resetControlPoints();
    }
  }
  setActivity(activity: boolean) {
    this.activity = activity;
  }
  destroy() {
    this.target.removeEventListener("mousemove", this.handleMouseMove);
    this.target.removeEventListener("mousedown", this.handleMouseDown);
    this.target.removeEventListener("mouseup", this.handleMouseUp);
    this.target.removeEventListener("dblclick", this.handleDoubleClick);
    this.clear();
  }
  initInteractor() {
    this.handleMouseMove = throttle(this.handleMouseMove, 16, this);
    this.handleMouseDown = this.handleMouseDown.bind(this);
    this.handleMouseUp = this.handleMouseUp.bind(this);
    this.handleDoubleClick = this.handleDoubleClick.bind(this);
    this.target.addEventListener("mousedown", this.handleMouseDown);
    this.target.addEventListener("mouseup", this.handleMouseUp);
    this.target.addEventListener("mousemove", this.handleMouseMove);
    this.target.addEventListener("dblclick", this.handleDoubleClick);
  }
  handleDoubleClick(e: MouseEvent) {
    if (!this.activity) {
      return;
    }
    // 去除缩放对鼠标获取点坐标的影响
    const scale = getScale();
    const x = e.offsetX / scale;
    const y = e.offsetY / scale;
    const hoverRes = this.findControlPointsHover(x, y);
    if (hoverRes) {
      this.currentControlPoint = hoverRes;
      this.drawBezierControlPoints();
    }
  }
  handleMouseDown(e: MouseEvent) {
    if (!this.activity) {
      return;
    }
    // 防止双击时执行两次
    const time = Date.now();
    const interval = time - this.mouseDownTime;
    if (interval < this.interval) {
      return;
    }
    // 去除缩放对鼠标获取点坐标的影响
    const scale = getScale();
    this.mouseDownTime = time;
    this.mouseStartPosition.x = e.offsetX / scale;
    this.mouseStartPosition.y = e.offsetY / scale;
    this.isLeftKeyDown = true;
    this.dragStart();
  }
  handleMouseUp(e: MouseEvent) {
    if (!this.activity) {
      return;
    }
    // 防止双击时执行两次
    const time = Date.now();
    const interval = time - this.mouseUpTime;
    if (interval < this.interval) {
      return;
    }
    this.mouseUpTime = time;
    this.dragEnd();
    this.mouseStartPosition.x = 0;
    this.mouseStartPosition.y = 0;
    this.isLeftKeyDown = false;
  }
  handleMouseMove(e: MouseEvent) {
    if (!this.activity) {
      return;
    }
    // 去除缩放对鼠标获取点坐标的影响
    const scale = getScale();
    const x = e.offsetX / scale;
    const y = e.offsetY / scale;
    if (this.isLeftKeyDown && this.hoverBezierControlPoint) {
      // 左键按在贝塞尔曲线控制点上,是移动控制点
      this.moveBezierControlPoint(x, y);
      return;
    }
    if (this.isLeftKeyDown && this.hoverControlPoint) {
      // 左键按在控制点上,是移动控制点
      this.moveControlPoint(x, y);
      return;
    }
    if (
      this.isLeftKeyDown &&
      !this.hoverBezierControlPoint &&
      !this.hoverControlPoint &&
      this.controlTarget
    ) {
      // 左键按在图元上,是拖拽图元
      this.drag(x - this.mouseStartPosition.x, y - this.mouseStartPosition.y);
      return;
    }
    // 鼠标是否在控制点上
    const hoverRes = this.findControlPointsHover(x, y);
    if (hoverRes) {
      this.hoverControlPoint = hoverRes;
      this.setCursor("move");
    } else {
      this.hoverControlPoint = null;
    }
    // 记录鼠标当前是否在贝塞尔曲线的控制点上
    let isHoverBezierControlPoint = false;
    if (this.preControlPoint.contain(x, y)) {
      this.hoverBezierControlPoint = {
        type: "pre",
        target: this.preControlPoint,
      };
      isHoverBezierControlPoint = true;
    }
    if (this.nextControlPoint.contain(x, y)) {
      this.hoverBezierControlPoint = {
        type: "next",
        target: this.nextControlPoint,
      };
      isHoverBezierControlPoint = true;
    }
    if (!isHoverBezierControlPoint) {
      this.hoverBezierControlPoint = null;
    }
  }
  getHoverPoint() {
    if (this.hoverControlPoint) {
      return this.hoverControlPoint.target;
    } else if (this.hoverBezierControlPoint) {
      return this.hoverBezierControlPoint.target;
    }
  }
  // 更新控制点的位置
  updateControlPoints(rect: {
    x: number;
    y: number;
    width: number;
    height: number;
  }) {
    this.resetControlPoints();
    this.drawControlPoints();
    if (this.hoverBezierControlPoint) {
      this.drawBezierControlPoints();
    }
  }
  // 重绘控制点
  drawControlPoints() {
    if (!this.activity) {
      return;
    }
    if (this.ctx) {
      const scale = getScale();
      this.ctx.save();
      this.ctx.scale(scale, scale);
      this.pointMap.forEach((point: RectNodeGraph) => {
        point.draw();
      });
      this.ctx.restore();
    }
  }
  // 重绘贝塞尔控制点
  drawBezierControlPoints() {
    if (!this.currentControlPoint || !this.controlTarget) {
      return;
    }
    if (!this.ctx) {
      return;
    }
    const scale = getScale();
    this.ctx.save();
    this.ctx.scale(scale, scale);
    const currentPoint =
      this.controlTarget.points[this.currentControlPoint.index];
    if (
      this.currentControlPoint.index !==
      this.controlTarget.points.length - 1
    ) {
      // 最后一个点没有后控制点
      // 绘制后控制点
      const nextPoint =
        this.controlTarget.points[this.currentControlPoint.index + 1];
      this.nextControlPoint.setPosition(nextPoint.x, nextPoint.y);
      this.ctx.moveTo(currentPoint.x, currentPoint.y);
      this.ctx.lineTo(nextPoint.x, nextPoint.y);
      this.ctx.stroke();
      this.nextControlPoint.draw();
    }
    if (this.currentControlPoint.index !== 0) {
      // 第一个点没有前控制点
      // 绘制前控制点
      const prePoint =
        this.controlTarget.points[this.currentControlPoint.index - 1];
      this.preControlPoint.setPosition(prePoint.x, prePoint.y);
      this.ctx.moveTo(currentPoint.x, currentPoint.y);
      this.ctx.lineTo(prePoint.x, prePoint.y);
      this.ctx.stroke();
      this.preControlPoint.draw();
    }
    this.ctx.restore();
  }
  // 判断鼠标是否悬浮在控制点上
  findControlPointsHover(
    x: number,
    y: number
  ): null | { index: number; target: RectNodeGraph } {
    let result = null;
    this.pointMap.forEach((point: RectNodeGraph, index: number) => {
      if (point.contain(x, y)) {
        result = {
          index: index,
          target: point,
        };
      }
    });
    return result;
  }
  dragStart() {}
  // 拖拽图元执行
  drag(translateX: number, translateY: number) {
    this.emit("drag", [translateX, translateY]);
    this.emit("redraw");
    this.drawControlPoints();
  }
  dragEnd() {
    if (this.isLeftKeyDown && this.controlTarget) {
      this.emit("drop");
    }
  }
  // 拖拽控制点变化位置尺寸时执行
  moveControlPoint(x: number, y: number) {
    if (!this.hoverControlPoint) {
      return;
    }
    this.hoverControlPoint.target.setPosition(x, y);
    this.controlTarget?.points[this.hoverControlPoint.index].setX(x);
    this.controlTarget?.points[this.hoverControlPoint.index].setY(y);
    this.emit("redraw");
    this.drawControlPoints();
    this.controlTarget?.saveOriginPoints();
  }
  // 移动贝塞尔曲线的控制点
  moveBezierControlPoint(x: number, y: number) {
    if (
      this.hoverBezierControlPoint &&
      this.currentControlPoint &&
      this.controlTarget
    ) {
      const type = this.hoverBezierControlPoint.type;
      const target = this.hoverBezierControlPoint.target;
      let controlPoint;
      let anotherControlPoint;
      let currentPoint =
        this.controlTarget.points[this.currentControlPoint.index];
      if (type === "pre") {
        controlPoint =
          this.controlTarget.points[this.currentControlPoint.index - 1];
        anotherControlPoint =
          this.controlTarget.points[this.currentControlPoint.index + 1];
      } else if (type === "next") {
        controlPoint =
          this.controlTarget.points[this.currentControlPoint.index + 1];
        anotherControlPoint =
          this.controlTarget.points[this.currentControlPoint.index - 1];
      }
      // 重置前后控制点坐标,保证前后控制点和当前点在同一条直线上
      target.setPosition(x, y);
      controlPoint?.setX(x);
      controlPoint?.setY(y);
      anotherControlPoint?.setX(2 * currentPoint.x - x);
      anotherControlPoint?.setY(2 * currentPoint.y - y);
      this.emit("redraw");
      this.drawControlPoints();
      this.drawBezierControlPoints();
      this.controlTarget?.saveOriginPoints();
    }
  }
}
